using System;
using System.ComponentModel;
using System.Linq.Expressions;

using NUnit.Framework;

using Rhino.Mocks;
using Rhino.Mocks.Interfaces;

using TrueCode.Core;
using TrueCode.Core.Aspects;

namespace TrueCode.Testing
{
  public static class MiscExtensions
  {
    public static void AssertPropertyChanged([NotNull] this PropertyChangedEventHandler eventHandler, string property, Action<IMethodOptions <object> > constraints = null )
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");

      eventHandler.AssertWasCalled(h => h.Invoke(Arg<object>.Is.Anything, Arg<PropertyChangedEventArgs>.Matches(args =>
         args.PropertyName == property)), constraints);
    }

    public static void AssertPropertyChanged<T>([NotNull] this PropertyChangedEventHandler eventHandler, [NotNull] Expression< Func< T > > property, Action<IMethodOptions <object> > constraints = null )
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");
      if (property == null) throw new ArgumentNullException("property");

      eventHandler.AssertWasCalled(h => h.Invoke(Arg<object>.Is.Anything, Arg<PropertyChangedEventArgs>.Matches(args =>
         args.PropertyName == nameof.Property(property))), constraints);
    }

    public static void AssertPropertyNotChanged<T>([NotNull] this PropertyChangedEventHandler eventHandler, [NotNull] Expression< Func< T > > property, Action<IMethodOptions <object> > constraints = null )
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");
      if (property == null) throw new ArgumentNullException("property");

      eventHandler.AssertWasNotCalled(h => h.Invoke(Arg<object>.Is.Anything, Arg<PropertyChangedEventArgs>.Matches(args =>
         args.PropertyName == nameof.Property(property))), constraints);
    }

    public static void AssertPropertyChanged<T>([NotNull] this PropertyChangedEventHandler eventHandler, [NotNull] Expression< Func< T > > property, T expectedValue, string assertMessage = null )
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");
      if (property == null) throw new ArgumentNullException("property");

      eventHandler.AssertWasCalled(h => h.Invoke(Arg<object>.Is.Anything, Arg<PropertyChangedEventArgs>.Matches(args =>
         args.PropertyName == nameof.Property(property))), null);

      var func = property.Compile();
      Assert.AreEqual(expectedValue, func(), assertMessage);
    }

    public static void AssertPropertyChanged<T>([NotNull] this PropertyChangedEventHandler eventHandler, [NotNull] Expression< Func< T > > property, [NotNull] Action<T> verifyPropertyValue, Action<IMethodOptions <object> > constraints = null)
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");
      if (property == null) throw new ArgumentNullException("property");
      if (verifyPropertyValue == null) throw new ArgumentNullException("verifyPropertyValue");

      eventHandler.AssertWasCalled(h => h.Invoke(Arg<object>.Is.Anything, Arg<PropertyChangedEventArgs>.Matches(args =>
         args.PropertyName == nameof.Property(property))), constraints);

      var func = property.Compile();
      verifyPropertyValue(func());
    }

    public static void AssertWasRaised([NotNull] this EventHandler eventHandler, Action<IMethodOptions <object> > constraints )
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");

      eventHandler.AssertWasCalled(x => x.Invoke(null, null), constraints);
    }

    public static void AssertWasRaised([NotNull] this EventHandler eventHandler)
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");

      eventHandler.AssertWasCalled(x => x.Invoke(null, null), c => c.IgnoreArguments());
    }


    public static void AssertWasNotRaised([NotNull] this EventHandler eventHandler)
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");

      eventHandler.AssertWasNotCalled(x => x.Invoke(null, null), c => c.IgnoreArguments());
    }

    public static void AssertWasRaised<T>([NotNull] this EventHandler<T, EventArgs> eventHandler, T expectedSender ) where T : class
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");

      eventHandler.AssertWasCalled(x => x.Invoke(Arg<T>.Is.Equal( expectedSender ), Arg<EventArgs>.Is.Anything) );
    }

    public static void AssertWasNotRaised<T>([NotNull] this EventHandler<T, EventArgs> eventHandler ) where T : class
    {
      if (eventHandler == null) throw new ArgumentNullException("eventHandler");

      eventHandler.AssertWasNotCalled(x => x.Invoke(null, null), c => c.IgnoreArguments() );
    }

    public static void RaiseEvent<TEventSource, T>(this TEventSource mockObject, Action<TEventSource> eventSubscription, T args) where TEventSource : class
    {
      mockObject.Raise(eventSubscription, mockObject, new EventArgs<T>(args));
    }

    public static void RaiseEvent<TEventSource>(this TEventSource mockObject, Action<TEventSource> eventSubscription) where TEventSource : class
    {
      mockObject.Raise(eventSubscription, mockObject, EventArgs.Empty);
    }
  }
}